#pragma once

#include "Lexer.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <format>
#include <list>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

namespace ztl{
    struct AST{
        struct Literal{
            using LiteralType = std::variant<int64_t>;
            LiteralType value;
        };
        struct Identifier{
            std::string name;
        };
        struct BinaryExpression{
            enum OperatorType{
                Add,
            };
            OperatorType Operator;
            using LRType = std::variant<
                            Identifier,
                            std::unique_ptr<BinaryExpression>
                            >;
            LRType left;
            LRType right;
        };
        struct UnaryExpression{
            enum struct Operator{
                Not='!',
                Minus='-'
            }op;
            std::variant<Identifier,Literal> right;
        };
        struct Expression{
            std::variant<BinaryExpression,Identifier,UnaryExpression> son;
        };
        struct VariableDeclaration{
            Identifier id;
            BinaryExpression init;
        };
        struct BlockStatement{
            using BodyType = std::vector<std::variant<VariableDeclaration>>;
            BodyType body;
        };
        auto getExpr(std::vector<Token> &tokens,int64_t begin,int64_t end)->std::tuple<VariableDeclaration,int64_t>{
            std::list<std::variant<Token,BinaryExpression,UnaryExpression,Literal,Identifier>> l(tokens.begin()+begin,tokens.begin()+end);
            for (auto it = l.begin();it!=l.end();it++) { // 转化字面量和变量名
                if(std::holds_alternative<Token>(*it)){
                    if(std::get<Token>(*it).type==ztl::TokenType::Identifier){
                        (*it)=Identifier{
                            .name = std::get<Token>(*it).str
                        };
                    }else if(std::get<Token>(*it).type==ztl::TokenType::Literal){
                        (*it)=Literal{
                            .value = atoi(std::get<Token>(*it).str.data())
                        };
                    }else if(std::get<Token>(*it).type==ztl::TokenType::Operator){
                        continue;
                    }else{
                        std::runtime_error("only Identifier Literal Operator can be put into a Expr");
                    }
                }else{
                    std::runtime_error("unknown type when AST");
                }
            }
            for(auto it = l.begin();it!=l.end();it++){ // 一元表达式
                if(!(std::holds_alternative<Token>(*it)))continue;
                auto nit = it;
                nit++;
                if(!(nit!=l.end()&&(std::holds_alternative<Literal>(*nit)||std::holds_alternative<Identifier>(*nit))))throw std::runtime_error(std::format("no char after char '{}' when AST or no value after it",std::get<Token>(*it).str));
                std::variant<Identifier,Literal> right;
                right = std::visit([](auto &i){
                    using T = std::decay_t<decltype(i)> ;
                    if constexpr(std::is_same_v<Literal, T>||std::is_same_v<Identifier, T>){
                        return std::variant<Identifier, Literal>(i);
                    }else{
                        throw std::runtime_error("cannot get type when AST");
                    }
                    std::unreachable();
                    return std::variant<Identifier, Literal>();
                },
                *nit);
                l.erase(nit);
                if(std::get<Token>(*it).str=="-"){
                    (*it) = UnaryExpression{
                        .op = UnaryExpression::Operator::Minus,
                        .right = right
                    };
                    continue;
                }else if(std::get<Token>(*it).str=="!"){
                    (*it) = UnaryExpression{
                        .op = UnaryExpression::Operator::Not,
                        .right = right
                    };
                    continue;
                }
                else{
                    throw std::runtime_error(std::format("unknown UnaryExpression char '{}'",std::get<Token>(*it).str));
                }
            }
            for(auto it = l.begin();it!=l.end();it++){ //二元表达式*
                if(std::holds_alternative<UnaryExpression>(*it)||std::holds_alternative<Literal>(*it)||std::holds_alternative<Identifier>(*it)){
                    auto nit = it;
                    nit++;
                    if(nit!=l.end()&&std::holds_alternative<Token>(*nit)&&(std::get<Token>(*nit).str=="*"||std::get<Token>(*nit).str=="/"))
                }else{
                    throw std::runtime_error("cannot get type when AST");
                }
            }
        }
        AST(std::vector<Token> tokens){
            
            for(size_t i=0;i<tokens.size();i++){
                if(tokens[i].str=="int"){

                }else{
                    throw std::runtime_error(std::format("unknown token '{}' when AST",tokens[i].str));
                }
            }
        }
    };
}